﻿using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Flurl.Http;
using Flurl.Http.Configuration;
using Microsoft.Extensions.Logging;
using Polly;
using Polly.Retry;
using Polly.Timeout;
using Polly.Wrap;

namespace suirui.ZhuMu.Middlewares
{
    /// <summary>
    /// 来自
    /// https://mp.weixin.qq.com/s/8MISeLdk6JLCQSahOmw0ZQ
    /// </summary>
    public class Policies
    {
        private readonly ILogger<Policies> _logger;

        public Policies(ILogger<Policies> logger)
        {
            _logger = logger;
        }

        private AsyncTimeoutPolicy<HttpResponseMessage> TimeoutPolicy
        {
            get
            {
                return Policy.TimeoutAsync<HttpResponseMessage>(3, (context, span, task) =>
                {
                    _logger.LogInformation($"Policy: Timeout delegate fired after {span.Seconds} seconds");
                    return Task.CompletedTask;
                });
            }
        }

        private AsyncRetryPolicy<HttpResponseMessage> RetryPolicy
        {
            get
            {
                HttpStatusCode[] retryStatus =
                {
                    HttpStatusCode.NotFound,
                    HttpStatusCode.ServiceUnavailable,
                    HttpStatusCode.RequestTimeout
                };
                return Policy
                    .HandleResult<HttpResponseMessage>(r => retryStatus.Contains(r.StatusCode))
                    .Or<TimeoutRejectedException>()
                    .WaitAndRetryAsync(new[]
                    {
                        // 表示重试3次，第一次1秒后重试，第二次2秒后重试，第三次4秒后重试
                        TimeSpan.FromSeconds(1),
                        TimeSpan.FromSeconds(2),
                        TimeSpan.FromSeconds(4)
                    }, (result, span, count, context) =>
                    {
                        _logger.LogInformation($"Policy: Retry delegate fired, attempt {count}");
                    });
            }
        }

        public AsyncPolicyWrap<HttpResponseMessage> PolicyStrategy =>
            Policy.WrapAsync(RetryPolicy, TimeoutPolicy);
    }

    public class PolicyHandler : DelegatingHandler
    {
        private readonly Policies _policies;

        public PolicyHandler(Policies policies)
        {
            _policies = policies;
        }

        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            return _policies.PolicyStrategy.ExecuteAsync(ct => base.SendAsync(request, ct), cancellationToken);
        }
    }

    public class PollyHttpClientFactory : DefaultHttpClientFactory
    {
        private readonly Policies _policies;

        public PollyHttpClientFactory(Policies policies)
        {
            _policies = policies;
        }

        public override HttpMessageHandler CreateMessageHandler()
        {
            return new PolicyHandler(_policies)
            {
                InnerHandler = base.CreateMessageHandler()
            };
        }
    }

    public class TestPolly
    {
        public async void Test()
        {
            var time = await Policy
    .Handle<FlurlHttpException>()
    .OrResult<IFlurlResponse>(r => !r.ResponseMessage.IsSuccessStatusCode)
    .WaitAndRetryAsync(new[]
                       {
                           TimeSpan.FromSeconds(1),
                           TimeSpan.FromSeconds(2),
                           TimeSpan.FromSeconds(4)
                       }, (result, span, count, context) =>
                       {
                           //_logger.LogInformation(count.ToString());
                       })
    .ExecuteAsync(() => "http://127.0.0.1:5000/api/polly".WithTimeout(3).GetAsync())
    .ReceiveJson();

            //_logger.LogInformation($"App: success - {time.time}");
        }
    }
}